﻿using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
using WindowsAPI;

namespace GDI
{
    public partial class ExtendedForm : Form
    {
        private const int INT_cpExStyle = 0x00080000;
        /* ------------------------------------------------------------------------------------------ */

        #region :: Win32 ::
        private static class Win32
        {
            public const int ULW_COLORKEY = 0x00000001;
            public const int ULW_ALPHA = 0x00000002;
            public const int ULW_OPAQUE = 0x00000004;

            public const byte AC_SRC_OVER = 0x00;
            public const byte AC_SRC_ALPHA = 0x01;
        }
        #endregion

        /* ------------------------------------------------------------------------------------------ */

        protected override CreateParams CreateParams
        {
            get
            {
                if(!DesignMode)
                {
                    var cp = base.CreateParams;
                    cp.ExStyle |= INT_cpExStyle; // WS_EX_LAYERED
                    return cp;
                }
                return base.CreateParams;
            }
        }

        /* ------------------------------------------------------------------------------------------ */

        public ExtendedForm() { InitializeComponent(); }

        /* ------------------------------------------------------------------------------------------ */

        public void SetBitmap(Bitmap bitmap) { SetBitmap(bitmap, 255); }

        public void SetBitmap(Bitmap bitmap, byte opacity)
        {
            if(bitmap.PixelFormat != PixelFormat.Format32bppArgb)
                throw new ApplicationException("The bitmap must be 32ppp with alpha-channel.");

            // Идея этого очень проста,
            // 1. Создаем совместимый контекст графического устройсва с экраном;
            // 2. Выбираем наш битмап с альфаканалом в контексте;
            // 3. Вызываем метод UpdateLayeredWindow.

            var screenDc = User32.GetDC(IntPtr.Zero);
            //var memDc = Win32.CreateCompatibleDC(screenDc);
            var memDc = GDI32.CreateCompatibleDC(screenDc);
            var hBitmap = IntPtr.Zero;
            var oldBitmap = IntPtr.Zero;

            try
            {
                // Получаем GDI хендл из GDI+ bitmap
                hBitmap = bitmap.GetHbitmap(Color.FromArgb(0));
                oldBitmap = GDI32.SelectObject(memDc, hBitmap);

                // Инициализируем необходимые Win32 структуры
                var size = bitmap.Size;
                var pointSource = new Point();
                var topPos = new Point(Left, Top);
                var blend = new BlendFunction
                            {
                                BlendOp = Win32.AC_SRC_OVER,
                                BlendFlags = 0,
                                SourceConstantAlpha = opacity,
                                AlphaFormat = Win32.AC_SRC_ALPHA
                            };
                // Вызываем метод, который собственно и нарисует все как надо :)
                User32.UpdateLayeredWindow(Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, 0,
                    ref blend, Win32.ULW_ALPHA);
            }
            finally
            {
                // По окончанию работы метода, освобождаем контекст
                User32.ReleaseDC(IntPtr.Zero, screenDc);
                if(hBitmap != IntPtr.Zero)
                {
                    // И удаляем объекты из памяти
                    GDI32.SelectObject(memDc, oldBitmap);
                    GDI32.DeleteObject(hBitmap);
                }
                GDI32.DeleteDC(memDc);
            }
        }

        /* ------------------------------------------------------------------------------------------ */
    }
}
